package com.springsource.a11AspectAndAdvisor;

import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

@Slf4j
public class ConvertAspectToAdvisor {

    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }

        public void after() {
            System.out.println("after");
        }

        @AfterReturning("execution(* foo())")
        public void afterReturning() {
            System.out.println("afterReturning");
        }

        @AfterThrowing("execution(* foo())")
        public void afterThrowing() {
            System.out.println("afterThrowing");
        }

        @Around("execution(* foo())")
        public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
            System.out.println("around before");
            Object proceed = joinPoint.proceed();
            System.out.println("around after");
            return proceed;
        }

    }

    static class Target {
        public void foo() {
            System.out.println("foo");
        }
    }

    public static void main(String[] args) throws Throwable {
        AspectInstanceFactory aspectInstanceFactory = new SingletonAspectInstanceFactory(new Aspect());
        Method[] methods = Aspect.class.getMethods();
        List<Advisor> advisorList = new ArrayList<>();
        // 解析注解
        for (Method method : methods) {
            if (method.isAnnotationPresent(Before.class)) {
                String expression = method.getAnnotation(Before.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, aspectInstanceFactory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                advisorList.add(advisor);
            } else if (method.isAnnotationPresent(AfterReturning.class)) {
                String expression = method.getAnnotation(AfterReturning.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, aspectInstanceFactory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                advisorList.add(advisor);
            } else if (method.isAnnotationPresent(Around.class)) {
                String expression = method.getAnnotation(Around.class).value();
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(expression);
                AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, aspectInstanceFactory);
                DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
                advisorList.add(advisor);
            }
        }
        for (Advisor advisor : advisorList) {
            System.out.println(advisor);
        }
        log.error("-----------------------------------------");
        // 转换成Interceptor
        Target target = new Target();
        Class<Target> targetClass = Target.class;
        Method foo = targetClass.getMethod("foo");
        ProxyFactory proxyFactory = new ProxyFactory(target);
        // 这一行很关键，否则会抛出异常，因为在MethodInterceptor调用的过程中，MethodInterceptor可能会使用到methodInterceptorList
        // ExposeInvocationInterceptor也是一个环绕通知，是把methodInterceptorList放入ThreadLocal中
        proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);
        // 这里添加完advisorList之后，正常应该进行一个排序，这里省略了
        proxyFactory.addAdvisors(advisorList);
        List<Object> methodInteceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(foo, targetClass);
        for (Object o : methodInteceptorList) {
            System.out.println(o);
        }

        log.error("-----------------------------------------");
        // 创建并执行调用链，此步模拟调用链过程，是一个简单的递归
        // proceed()方法会调用执行链链中下一个环绕通知
        // 每个环绕通知内部继续调用proceed()
        // 调用到没有更多通知了，就调用目标方法
        Class<ReflectiveMethodInvocation> reflectiveMethodInvocationClass = ReflectiveMethodInvocation.class;
        Constructor<ReflectiveMethodInvocation> declaredConstructor = reflectiveMethodInvocationClass.getDeclaredConstructor(Object.class, Object.class, Method.class, Object[].class, Class.class, List.class);
        declaredConstructor.setAccessible(true);
        ReflectiveMethodInvocation reflectiveMethodInvocation = declaredConstructor.newInstance(null, target, foo, new Object[0], targetClass, methodInteceptorList);
        reflectiveMethodInvocation.proceed();

    }

}
